require(tidyverse)
theme_set(theme_bw())
options(scipen = 1, digits = 4)
helper_func_path = '/Users/zeynepenkavi/Dropbox/PoldrackLab/SRO_Retest_Analyses/code/helper_functions/'
source(paste0(helper_func_path, 'get_numeric_cols.R'))
source(paste0(helper_func_path, 'remove_outliers.R'))
source(paste0(helper_func_path, 'remove_correlated_task_variables.R'))
source(paste0(helper_func_path, 'transform_remove_skew.R'))
source(paste0(helper_func_path, 'get_demographics.R'))
source(paste0(helper_func_path, 'residualize_baseline.R'))
source(paste0(helper_func_path, 'find_optimal_components.R'))
ddm_workspace_scripts = '/Users/zeynepenkavi/Dropbox/PoldrackLab/SRO_DDM_Analyses/code/workspace_scripts/'
source(paste0(ddm_workspace_scripts,'ddm_measure_labels.R'))
source(paste0(ddm_workspace_scripts,'ddm_subject_data.R'))
rm(test_data_hddm_fullfit, test_data_hddm_refit)
fig_path = '/Users/zeynepenkavi/Dropbox/PoldrackLab/SRO_DDM_Analyses/output/figures/'
cbbPalette <- c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
To reduce the number of variables in a data-driven way (instead of just selecting the variables that went in to the ontology paper) I’ll apply the cleaning methods from the ontology pipeline:
- transformation of non-normal variables (should be particularly useful for a set of variables with many response times) and
- dropping variables with r>0.85.
Extract EZ variables
test_data_ez = test_data %>%
select(grep('EZ', names(test_data), value=T))
Remove variables that are correlated >0.85
clean_test_data_ez = remove_correlated_task_variables(test_data_ez)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Dropping 0 variables with correlations above 0.85
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Remove outliers (>2.5 SD away)
clean_test_data_ez = as.data.frame(apply(clean_test_data_ez, 2, remove_outliers))
Transform skewed variables
#Nothing to transform
numeric_cols = get_numeric_cols()
numeric_cols = numeric_cols[numeric_cols %in% names(clean_test_data_ez) == T]
clean_test_data_ez = transform_remove_skew(clean_test_data_ez, numeric_cols)
Drop subject identifier column, mean impute and drop cols with no variance
clean_test_data_ez_std = clean_test_data_ez %>% mutate_if(is.numeric, scale)
#mean imputation
clean_test_data_ez_std[is.na(clean_test_data_ez_std)]=0
#drop cols with no variance
clean_test_data_ez_std = clean_test_data_ez_std %>%
select_if(function(col) sd(col) != 0)
Residualize Age and Sex effects
data_path = '/Users/zeynepenkavi/Documents/PoldrackLabLocal/Self_Regulation_Ontology/Data/'
release = 'Complete_03-29-2018/'
dataset = 'demographic_health.csv'
demographics = get_demographics(dataset = paste0(data_path, release, dataset))
demographics = demographics %>%
filter(X %in% test_data$sub_id)
clean_test_data_ez_std = cbind(clean_test_data_ez_std, demographics[,c("Age", "Sex")])
res_clean_test_data_ez = residualize_baseline(clean_test_data_ez_std)
PCA on EZ variables of test data
Number of variables analysis
In this dataset there are more variables than observations.
Given the large number of measures some can possible be represented as linear combinations as others.
This would result in 0 or negative values in the eigenvalues of the correlation matrix and would mean that the correlation matrix is not positive definite.
It is hard to detect such multiple dependencies. Given the number of possible combinations of variables from all possible variables it is also impossible to compute the exact largest combination of variables that has a positive definite correlation matrix.
So instead I used a sampling approach. Below is a plot of the proportion of positive definite matrices out of 1000 samples of the given numbers of variables drawn from all variables.
The plot shows that there is a steady decrease in the proportion of positive definite matrices the more variables are used. It sharply drops to 0 from chance at 160 variables.
max_num_vars_summary = read.csv('/Users/zeynepenkavi/Dropbox/PoldrackLab/SRO_DDM_Analyses/input/max_num_vars_summary.csv')
p = max_num_vars_summary %>%
ggplot(aes(as.factor(cur_num_vars), prop_det_pos))+
geom_point()+
geom_line(group=1)+
xlab("Number of variables drawn")+
ylab("Proportion of positive definite correlation matrices")+
theme(axis.text.x = element_text(angle = 90))
ggsave('max_num_vars.jpeg',plot=p, device = 'jpeg', path = fig_path, width =12, height = 4, units = "in")
p

This means that any effort to reduce dimensionality using an eigenvalue decomposition will encounter problems.
For numerical efficiency and stability many PCA applications use singular value decomposition instead (e.g. see documentation for the princomp and prcomp functions in the base package).
To be consistent with the dimensionality reduction methods used in the ontology paper I will use the psych package functions for PCA and EFA. These rely on an eigenvalue decomposition but have a built-in function to smooth over negative eigenvalues that prints a warning but continues to execute and yield a solution.
But to confirm that the resulting factors are not problematic I will run an SVD and recover the same parameters as well.
Eigen on cor matrix
Eigenvalue decomposition on correlation matrix.
ez_t1_pca_3 = principal(res_clean_test_data_ez, nfactors=3, rotate="oblimin")
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The determinant of the smoothed correlation was zero.
This means the objective function is not defined.
Chi square is based upon observed residuals.
The determinant of the smoothed correlation was zero.
This means the objective function is not defined for the null model either.
The Chi square is thus based upon observed correlations.
Warning in principal(res_clean_test_data_ez, nfactors = 3, rotate =
"oblimin"): The matrix is not positive semi-definite, scores found from
Structure loadings
Scree plot of first 10 components
data.frame(ez_t1_pca_3$values) %>%
rename(eig = ez_t1_pca_3.values) %>%
arrange(-eig) %>%
mutate(var_pct = eig/sum(eig)*100,
pc = 1:n()) %>%
filter(pc<11)%>%
ggplot(aes(factor(pc), var_pct))+
geom_bar(stat="identity")+
ylab("Percentage of variance explained")+
xlab("Principal component")

Difference in percentage of variance explained is below 1% after the 3 component.
data.frame(ez_t1_pca_3$values) %>%
rename(eig = ez_t1_pca_3.values) %>%
arrange(-eig) %>%
mutate(var_pct = eig/sum(eig)*100,
pc = 1:n(),
var_pct_shift = lead(var_pct),
var_pct_diff = var_pct - var_pct_shift)
Visualizing the first three coponents and coloring them by the parameter type.
ez_t1_pca_3_loadings = as.data.frame(ez_t1_pca_3$loadings[])
ez_t1_pca_3_loadings[abs(ez_t1_pca_3_loadings)<0.3]=NA
tmp = ez_t1_pca_3_loadings %>%
mutate(dv = row.names(.)) %>%
select(dv, TC1, TC2, TC3) %>%
mutate(num_loading = 3-(is.na(TC1)+is.na(TC2)+is.na(TC3) ) ) %>%
filter(num_loading!=0) %>%
select(-num_loading) %>%
arrange(-TC1, -TC2, -TC3) %>%
mutate(order_num = 1:n(),
dv = reorder(dv, -order_num)) %>%
select(-order_num) %>%
gather(Factor, Loading, -dv) %>%
na.exclude() %>%
mutate(neg_load = factor(ifelse(Loading>0,"NA","#000000")),
var_type = ifelse(grepl("drift", dv), "drif rate",
ifelse(grepl("thresh", dv), "threshold",
ifelse(grepl("non_dec", dv), "non-decision", NA))))
p = tmp%>%
ggplot(aes(dv, abs(Loading), fill=var_type, col = neg_load))+
geom_bar(stat = "identity")+
facet_wrap(~Factor, nrow=1)+
coord_flip()+
xlab("")+
ylab("Absolute Loading")+
scale_fill_manual(values=cbbPalette)+
scale_color_identity()+
theme(legend.position = "bottom",
legend.title=element_blank(),
axis.text.y = element_blank())
ggsave('EZ_PCA_T1_3.jpeg',plot=p, device = 'jpeg', path = fig_path, width = 10, height = 12, units = "in")
p

SVD
Double checking the above factor solution using a singular value decomposition and oblimin rotation.
ez_svd <- svd(res_clean_test_data_ez)
ncomp = 3
df <- nrow(res_clean_test_data_ez) - 1
ez_t1_svd_rawLoadings = ez_svd$v[,1:ncomp] %*% diag(ez_svd$d/sqrt(df), ncomp, ncomp)
ez_t1_svd_rotatedLoadings <- GPArotation::oblimin(ez_t1_svd_rawLoadings)$loadings
ez_t1_svd_rotatedLoadings[abs(ez_t1_svd_rotatedLoadings)<0.3]=NA
tmp = data.frame(ez_t1_svd_rotatedLoadings) %>%
mutate(dv = names(res_clean_test_data_ez),
X1 = -1*X1,
X3= -1*X3) %>%
select(dv, X1, X2, X3)%>%
mutate(num_loading = 3-(is.na(X1)+is.na(X2)+is.na(X3) ) ) %>%
filter(num_loading!=0) %>%
select(-num_loading) %>%
arrange(-X1, -X2, -X3) %>%
mutate(order_num = 1:n(),
dv = reorder(dv, -order_num)) %>%
select(-order_num) %>%
gather(Factor, Loading, -dv) %>%
na.exclude() %>%
mutate(neg_load = factor(ifelse(Loading>0,"NA","#000000")),
var_type = ifelse(grepl("drift", dv), "drif rate",
ifelse(grepl("thresh", dv), "threshold",
ifelse(grepl("non_dec", dv), "non-decision", NA))))
p = tmp%>%
ggplot(aes(dv, abs(Loading), fill=var_type, col = neg_load))+
geom_bar(stat = "identity")+
facet_wrap(~Factor, nrow=1)+
coord_flip()+
xlab("")+
ylab("Absolute Loading")+
scale_fill_manual(values=cbbPalette)+
scale_color_identity()+
theme(legend.position = "bottom",
legend.title=element_blank(),
axis.text.y = element_blank())
ggsave('EZ_SVD_T1_3.jpeg',plot=p, device = 'jpeg', path = fig_path, width = 10, height = 12, units = "in")
p

EFA on EZ variables of test data
How many factors should be extracted from the EFA? To answer this we run models extracting 2 to 50 components and rank them by BIC.
efa_ez_t1_comp_metrics = find_optimal_components(res_clean_test_data_ez, fm = "minres", minc=2)
This suggests that a 16 component solution would be the best fitting model.
efa_ez_t1_comp_metrics
Fit the model suggested by the BIC comparison.
ez_t1_fa_16 = fa(res_clean_test_data_ez, efa_ez_t1_comp_metrics$comp[1], rotate='oblimin', fm='minres', scores='tenBerge')
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in GPFoblq(L, Tmat = Tmat, normalize = normalize, eps = eps, maxit
= maxit, : convergence not obtained in GPFoblq. 1000 iterations used.
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The determinant of the smoothed correlation was zero.
This means the objective function is not defined.
Chi square is based upon observed residuals.
The determinant of the smoothed correlation was zero.
This means the objective function is not defined for the null model either.
The Chi square is thus based upon observed correlations.
The estimated weights for the factor scores are probably incorrect. Try a different factor extraction method.
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
Fit the 3 factor model that is of theoretical interest.
ez_t1_fa_3 = fa(res_clean_test_data_ez, 3, rotate='oblimin', fm='minres', scores='tenBerge')
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The determinant of the smoothed correlation was zero.
This means the objective function is not defined.
Chi square is based upon observed residuals.
The determinant of the smoothed correlation was zero.
This means the objective function is not defined for the null model either.
The Chi square is thus based upon observed correlations.
The estimated weights for the factor scores are probably incorrect. Try a different factor extraction method.
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
Differences in BIC’s are difficult to interpret.
I tried to run a more formal model comparison between the 3 factor solution that we have theoretical reasons for and the 16 factor model that is selected based on BIC. Below is the result. I’m not quiet sure how to interpreted but it seems to suggest that the more complicated model is not significantly better.
anova(ez_t1_fa_3, ez_t1_fa_16)
So what would the 3 factor solution look like? Not surprisingly, very similar to the PC’s.
ez_t1_fa_3_loadings = as.data.frame(ez_t1_fa_3$loadings[])
ez_t1_fa_3_loadings[abs(ez_t1_fa_3_loadings)<0.3]=NA
tmp = ez_t1_fa_3_loadings %>%
mutate(dv = row.names(.)) %>%
select(dv, MR1, MR2, MR3) %>%
mutate(num_loading = 3-(is.na(MR1)+is.na(MR2)+is.na(MR3) ) ) %>%
filter(num_loading!=0) %>%
select(-num_loading) %>%
arrange(-MR1, -MR2, -MR3) %>%
mutate(order_num = 1:n(),
dv = reorder(dv, -order_num)) %>%
select(-order_num) %>%
gather(Factor, Loading, -dv) %>%
na.exclude() %>%
mutate(neg_load = factor(ifelse(Loading>0,"NA","#000000")),
var_type = ifelse(grepl("drift", dv), "drif rate",
ifelse(grepl("thresh", dv), "threshold",
ifelse(grepl("non_dec", dv), "non-decision", NA))))
p = tmp%>%
ggplot(aes(dv, abs(Loading), fill=var_type, col = neg_load))+
geom_bar(stat = "identity")+
facet_wrap(~Factor, nrow=1)+
coord_flip()+
xlab("")+
ylab("Absolute Loading")+
scale_fill_manual(values=cbbPalette)+
scale_color_identity()+
theme(legend.position = "bottom",
legend.title=element_blank(),
axis.text.y = element_blank())
ggsave('EZ_FA_T1_3.jpeg',plot=p, device = 'jpeg', path = fig_path, width = 10, height = 12, units = "in")
p

Are there notable differences in the 16 factor solution? (Looking at the first half of the model) The three parameter types seem to load on two factors each instead of one. These tend to reflect separations between tasks (e.g. one non-decision factor is just for the shape matching task while the other contains all the remaining ones). Since these differences are of less theoretical interest for this exercise I think choosing the three factor solution would be appropriate.
ez_t1_fa_16_loadings = as.data.frame(ez_t1_fa_16$loadings[])
ez_t1_fa_16_loadings[abs(ez_t1_fa_16_loadings)<0.3]=NA
tmp = ez_t1_fa_8_loadings %>%
mutate(dv = row.names(.)) %>%
select(dv, MR1, MR2, MR3, MR4, MR5, MR6, MR7, MR8) %>%
mutate(num_loading = 8-(is.na(MR1)+is.na(MR2)+is.na(MR3)+is.na(MR4)+is.na(MR5)+is.na(MR6)+is.na(MR7)+is.na(MR8) ) ) %>%
filter(num_loading!=0) %>%
select(-num_loading) %>%
arrange(-MR1, -MR2, -MR3,-MR1, -MR5, -MR6,-MR7, -MR8) %>%
mutate(order_num = 1:n(),
dv = reorder(dv, -order_num)) %>%
select(-order_num) %>%
gather(Factor, Loading, -dv) %>%
na.exclude() %>%
mutate(neg_load = factor(ifelse(Loading>0,"NA","#000000")),
var_type = ifelse(grepl("drift", dv), "drif rate",
ifelse(grepl("thresh", dv), "threshold",
ifelse(grepl("non_dec", dv), "non-decision", NA))))
p = tmp%>%
ggplot(aes(dv, abs(Loading), fill=var_type, col = neg_load))+
geom_bar(stat = "identity")+
facet_wrap(~Factor, nrow=1)+
coord_flip()+
xlab("")+
ylab("Absolute Loading")+
scale_fill_manual(values=cbbPalette)+
scale_color_identity()+
theme(legend.position = "bottom",
legend.title=element_blank())
#axis.text.y = element_blank())
ggsave('EZ_FA_T1_8.jpeg',plot=p, device = 'jpeg', path = fig_path, width = 10, height = 12, units = "in")
knitr::include_graphics(paste0(fig_path, 'EZ_FA_T1_8.jpeg'))

Based on these results I ran 3 factor EFA’s on the remaining data
EFA on HDDM variables of test data
Extract EZ variables
test_data_hddm = test_data %>%
select(grep('hddm', names(test_data), value=T))
Remove variables that are correlated >0.85
clean_test_data_hddm = remove_correlated_task_variables(test_data_hddm)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Dropping 0 variables with correlations above 0.85
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Remove outliers (>2.5 SD away)
clean_test_data_hddm = as.data.frame(apply(clean_test_data_hddm, 2, remove_outliers))
Transform skewed variables
#Nothing to transform
numeric_cols = get_numeric_cols()
numeric_cols = numeric_cols[numeric_cols %in% names(clean_test_data_hddm) == T]
clean_test_data_hddm = transform_remove_skew(clean_test_data_hddm, numeric_cols)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 data positively skewed data were transformed:
dot_pattern_expectancy.hddm_thresh
stim_selective_stop_signal.hddm_thresh
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
0 positively skewed data could not be transformed successfully:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Drop subject identifier column, mean impute and drop cols with no variance
clean_test_data_hddm_std = clean_test_data_hddm %>% mutate_if(is.numeric, scale)
#mean imputation
clean_test_data_hddm_std[is.na(clean_test_data_hddm_std)]=0
#drop cols with no variance
clean_test_data_hddm_std = clean_test_data_hddm_std %>%
select_if(function(col) sd(col) != 0)
Residualize Age and Sex effects
clean_test_data_hddm_std = cbind(clean_test_data_hddm_std, demographics[,c("Age", "Sex")])
res_clean_test_data_hddm = residualize_baseline(clean_test_data_hddm_std)
Fit the 3 factor model that is of theoretical interest.
hddm_t1_fa_3 = fa(res_clean_test_data_hddm, 3, rotate='oblimin', fm='minres', scores='tenBerge')
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The estimated weights for the factor scores are probably incorrect. Try a different factor extraction method.
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The three factor model does not work as well for HDDM’s because of the 106 variables we have 75 are drift rates, 17 are threshold and 14 are non-decision times. This is because while all the parameters are fit separately for EZ only the drift rate parameter is allowed to vary by condition for the HDDM. So the model is mostly separating out drift rates from each other.
hddm_t1_fa_3_loadings = as.data.frame(hddm_t1_fa_3$loadings[])
hddm_t1_fa_3_loadings[abs(hddm_t1_fa_3_loadings)<0.3]=NA
tmp = hddm_t1_fa_3_loadings %>%
mutate(dv = row.names(.)) %>%
select(dv, MR1, MR2, MR3) %>%
mutate(num_loading = 3-(is.na(MR1)+is.na(MR2)+is.na(MR3) ) ) %>%
filter(num_loading!=0) %>%
select(-num_loading) %>%
arrange(-MR1, -MR2, -MR3) %>%
mutate(order_num = 1:n(),
dv = reorder(dv, -order_num)) %>%
select(-order_num) %>%
gather(Factor, Loading, -dv) %>%
na.exclude() %>%
mutate(neg_load = factor(ifelse(Loading>0,"NA","#000000")),
var_type = ifelse(grepl("drift", dv), "drif rate",
ifelse(grepl("thresh", dv), "threshold",
ifelse(grepl("non_dec", dv), "non-decision", NA))))
p = tmp%>%
ggplot(aes(dv, abs(Loading), fill=var_type, col = neg_load))+
geom_bar(stat = "identity")+
facet_wrap(~Factor, nrow=1)+
coord_flip()+
xlab("")+
ylab("Absolute Loading")+
scale_fill_manual(values=cbbPalette)+
scale_color_identity()+
theme(legend.position = "bottom",
legend.title=element_blank(),
axis.text.y = element_blank())
ggsave('HDDM_T1_FA_3.jpeg',plot=p, device = 'jpeg', path = fig_path, width = 10, height = 12, units = "in")
p

Does it fit better if I use just the main drift rates and not the condition ones?
hddm_var_subset = c("adaptive_n_back.hddm_drift" , "attention_network_task.hddm_drift", "choice_reaction_time.hddm_drift", "directed_forgetting.hddm_drift", "dot_pattern_expectancy.hddm_drift" , "local_global_letter.hddm_drift", "motor_selective_stop_signal.hddm_drift", "recent_probes.hddm_drift", "shape_matching.hddm_drift" , "simon.hddm_drift", "stim_selective_stop_signal.hddm_drift", "stop_signal.hddm_drift", "stroop.hddm_drift" , "threebytwo.hddm_drift", "adaptive_n_back.hddm_thresh", "attention_network_task.hddm_thresh", "choice_reaction_time.hddm_thresh", "directed_forgetting.hddm_thresh", "dot_pattern_expectancy.hddm_thresh.logTr", "local_global_letter.hddm_thresh", "motor_selective_stop_signal.hddm_thresh", "recent_probes.hddm_thresh", "shape_matching.hddm_thresh", "simon.hddm_thresh", "stim_selective_stop_signal.hddm_thresh.logTr", "stop_signal.hddm_thresh", "stop_signal.hddm_thresh_high", "stop_signal.hddm_thresh_low", "stop_signal.proactive_slowing_hddm_thresh", "stroop.hddm_thresh", "threebytwo.hddm_thresh", grep("non_dec", names(test_data_hddm), value = T))
res_clean_test_data_hddm_subset = res_clean_test_data_hddm %>% select(hddm_var_subset)
hddm_t1_fa_3_subset = fa(res_clean_test_data_hddm_subset, 3, rotate='oblimin', fm='minres', scores='tenBerge')
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
Then it works better but still not doing a great job separating out thresholds from non-decision times.
hddm_t1_fa_3_subset_loadings = as.data.frame(hddm_t1_fa_3_subset$loadings[])
hddm_t1_fa_3_subset_loadings[abs(hddm_t1_fa_3_subset_loadings)<0.3]=NA
tmp = hddm_t1_fa_3_subset_loadings %>%
mutate(dv = row.names(.)) %>%
select(dv, MR1, MR2, MR3) %>%
mutate(num_loading = 3-(is.na(MR1)+is.na(MR2)+is.na(MR3) ) ) %>%
filter(num_loading!=0) %>%
select(-num_loading) %>%
arrange(-MR1, -MR2, -MR3) %>%
mutate(order_num = 1:n(),
dv = reorder(dv, -order_num)) %>%
select(-order_num) %>%
gather(Factor, Loading, -dv) %>%
na.exclude() %>%
mutate(neg_load = factor(ifelse(Loading>0,"NA","#000000")),
var_type = ifelse(grepl("drift", dv), "drif rate",
ifelse(grepl("thresh", dv), "threshold",
ifelse(grepl("non_dec", dv), "non-decision", NA))))
p = tmp%>%
ggplot(aes(dv, abs(Loading), fill=var_type, col = neg_load))+
geom_bar(stat = "identity")+
facet_wrap(~Factor, nrow=1)+
coord_flip()+
xlab("")+
ylab("Absolute Loading")+
scale_fill_manual(values=cbbPalette)+
scale_color_identity()+
theme(legend.position = "bottom",
legend.title=element_blank())
#axis.text.y = element_blank())
ggsave('HDDM_T1_FA_3_subset.jpeg',plot=p, device = 'jpeg', path = fig_path, width = 10, height = 12, units = "in")
knitr::include_graphics(paste0(fig_path, 'HDDM_T1_FA_3_subset.jpeg'))

EFA on EZ variables of retest data
Using the model from the test data to predict the retest data. Later will use factor scores from this to calculate reliability of reduced dimensions.
retest_data_ez = retest_data %>%
select(grep('EZ', names(retest_data), value=T))
Remove variables that are correlated >0.85
clean_retest_data_ez = remove_correlated_task_variables(retest_data_ez)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Dropping 0 variables with correlations above 0.85
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Remove outliers (>2.5 SD away)
clean_retest_data_ez = as.data.frame(apply(clean_retest_data_ez, 2, remove_outliers))
Transform skewed variables
#Nothing to transform
numeric_cols = get_numeric_cols()
numeric_cols = numeric_cols[numeric_cols %in% names(clean_retest_data_ez) == T]
clean_retest_data_ez = transform_remove_skew(clean_retest_data_ez, numeric_cols)
Drop subject identifier column, mean impute and drop cols with no variance
clean_retest_data_ez_std = clean_retest_data_ez %>% mutate_if(is.numeric, scale)
#mean imputation
clean_retest_data_ez_std[is.na(clean_retest_data_ez_std)]=0
#drop cols with no variance
clean_retest_data_ez_std = clean_retest_data_ez_std %>%
select_if(function(col) sd(col) != 0)
Residualize Age and Sex effects
clean_retest_data_ez_std = cbind(clean_retest_data_ez_std, demographics[,c("Age", "Sex")])
res_clean_retest_data_ez = residualize_baseline(clean_retest_data_ez_std)
Predict retest data using the 3 factor EFA from test data.
ez_t2_fa_3 = predict(ez_t1_fa_3, res_clean_retest_data_ez)
EFA on HDDM variables of retest data
retest_data_hddm = retest_data %>%
select(grep('hddm', names(retest_data), value=T))
Remove variables that are correlated >0.85
clean_retest_data_hddm = remove_correlated_task_variables(retest_data_hddm)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Dropping 0 variables with correlations above 0.85
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Remove outliers (>2.5 SD away)
clean_retest_data_hddm = as.data.frame(apply(clean_retest_data_hddm, 2, remove_outliers))
Transform skewed variables as in T1
clean_retest_data_hddm = clean_retest_data_hddm %>%
mutate(dot_pattern_expectancy.hddm_thresh.logTr = log(dot_pattern_expectancy.hddm_thresh),
stim_selective_stop_signal.hddm_thresh.logTr = log(stim_selective_stop_signal.hddm_thresh)) %>%
select(-dot_pattern_expectancy.hddm_thresh,-stim_selective_stop_signal.hddm_thresh)
Drop subject identifier column, mean impute and drop cols with no variance
clean_retest_data_hddm_std = clean_retest_data_hddm %>% mutate_if(is.numeric, scale)
#mean imputation
clean_retest_data_hddm_std[is.na(clean_retest_data_hddm_std)]=0
#drop cols with no variance
clean_retest_data_hddm_std = clean_retest_data_hddm_std %>%
select_if(function(col) sd(col) != 0)
Residualize Age and Sex effects
clean_retest_data_hddm_std = cbind(clean_retest_data_hddm_std, demographics[,c("Age", "Sex")])
res_clean_retest_data_hddm = residualize_baseline(clean_retest_data_hddm_std)
Select only the vars that went in to the limited set of hddm variables
res_clean_retest_data_hddm_subset = res_clean_retest_data_hddm %>%
select(hddm_var_subset)
Predict using the 3 factor EFA from T1
hddm_t2_fa_3_subset = predict(hddm_t1_fa_3_subset, res_clean_retest_data_hddm_subset)
LS0tCnRpdGxlOiAnRGlmZmVyZW50IGFwcHJvYWNoZXMgdG8gZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIG9mIERETSB2YXJpYWJsZXMnCm91dHB1dDoKZ2l0aHViX2RvY3VtZW50Ogp0b2M6IHllcwp0b2NfZmxvYXQ6IHllcwotLS0KCmBgYHtyfQpyZXF1aXJlKHRpZHl2ZXJzZSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCm9wdGlvbnMoc2NpcGVuID0gMSwgZGlnaXRzID0gNCkKCmhlbHBlcl9mdW5jX3BhdGggPSAnL1VzZXJzL3pleW5lcGVua2F2aS9Ecm9wYm94L1BvbGRyYWNrTGFiL1NST19SZXRlc3RfQW5hbHlzZXMvY29kZS9oZWxwZXJfZnVuY3Rpb25zLycKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAnZ2V0X251bWVyaWNfY29scy5SJykpCnNvdXJjZShwYXN0ZTAoaGVscGVyX2Z1bmNfcGF0aCwgJ3JlbW92ZV9vdXRsaWVycy5SJykpCnNvdXJjZShwYXN0ZTAoaGVscGVyX2Z1bmNfcGF0aCwgJ3JlbW92ZV9jb3JyZWxhdGVkX3Rhc2tfdmFyaWFibGVzLlInKSkKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAndHJhbnNmb3JtX3JlbW92ZV9za2V3LlInKSkKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAnZ2V0X2RlbW9ncmFwaGljcy5SJykpCnNvdXJjZShwYXN0ZTAoaGVscGVyX2Z1bmNfcGF0aCwgJ3Jlc2lkdWFsaXplX2Jhc2VsaW5lLlInKSkKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAnZmluZF9vcHRpbWFsX2NvbXBvbmVudHMuUicpKQoKZGRtX3dvcmtzcGFjZV9zY3JpcHRzID0gJy9Vc2Vycy96ZXluZXBlbmthdmkvRHJvcGJveC9Qb2xkcmFja0xhYi9TUk9fRERNX0FuYWx5c2VzL2NvZGUvd29ya3NwYWNlX3NjcmlwdHMvJwpzb3VyY2UocGFzdGUwKGRkbV93b3Jrc3BhY2Vfc2NyaXB0cywnZGRtX21lYXN1cmVfbGFiZWxzLlInKSkKc291cmNlKHBhc3RlMChkZG1fd29ya3NwYWNlX3NjcmlwdHMsJ2RkbV9zdWJqZWN0X2RhdGEuUicpKQpybSh0ZXN0X2RhdGFfaGRkbV9mdWxsZml0LCB0ZXN0X2RhdGFfaGRkbV9yZWZpdCkKCmZpZ19wYXRoID0gJy9Vc2Vycy96ZXluZXBlbmthdmkvRHJvcGJveC9Qb2xkcmFja0xhYi9TUk9fRERNX0FuYWx5c2VzL291dHB1dC9maWd1cmVzLycKY2JiUGFsZXR0ZSA8LSBjKCIjRTY5RjAwIiwgIiM1NkI0RTkiLCAiIzAwOUU3MyIsICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikKYGBgCgpUbyByZWR1Y2UgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgaW4gYSBkYXRhLWRyaXZlbiB3YXkgKGluc3RlYWQgb2YganVzdCBzZWxlY3RpbmcgdGhlIHZhcmlhYmxlcyB0aGF0IHdlbnQgaW4gdG8gdGhlIG9udG9sb2d5IHBhcGVyKSAgSSdsbCBhcHBseSB0aGUgY2xlYW5pbmcgbWV0aG9kcyBmcm9tIHRoZSBvbnRvbG9neSBwaXBlbGluZTogICAKLSB0cmFuc2Zvcm1hdGlvbiBvZiBub24tbm9ybWFsIHZhcmlhYmxlcyAoc2hvdWxkIGJlIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGEgc2V0IG9mIHZhcmlhYmxlcyB3aXRoIG1hbnkgcmVzcG9uc2UgdGltZXMpIGFuZCAgICAgCi0gZHJvcHBpbmcgdmFyaWFibGVzIHdpdGggcj4wLjg1LiAgCgpFeHRyYWN0IEVaIHZhcmlhYmxlcwoKYGBge3J9CnRlc3RfZGF0YV9leiA9IHRlc3RfZGF0YSAlPiUKICBzZWxlY3QoZ3JlcCgnRVonLCBuYW1lcyh0ZXN0X2RhdGEpLCB2YWx1ZT1UKSkKYGBgCgpSZW1vdmUgdmFyaWFibGVzIHRoYXQgYXJlIGNvcnJlbGF0ZWQgPjAuODUKCmBgYHtyfQpjbGVhbl90ZXN0X2RhdGFfZXogPSByZW1vdmVfY29ycmVsYXRlZF90YXNrX3ZhcmlhYmxlcyh0ZXN0X2RhdGFfZXopCmBgYAoKUmVtb3ZlIG91dGxpZXJzICg+Mi41IFNEIGF3YXkpCgpgYGB7cn0KY2xlYW5fdGVzdF9kYXRhX2V6ID0gYXMuZGF0YS5mcmFtZShhcHBseShjbGVhbl90ZXN0X2RhdGFfZXosIDIsIHJlbW92ZV9vdXRsaWVycykpCmBgYAoKVHJhbnNmb3JtIHNrZXdlZCB2YXJpYWJsZXMKCmBgYHtyfQojTm90aGluZyB0byB0cmFuc2Zvcm0KbnVtZXJpY19jb2xzID0gZ2V0X251bWVyaWNfY29scygpCm51bWVyaWNfY29scyA9IG51bWVyaWNfY29sc1tudW1lcmljX2NvbHMgJWluJSBuYW1lcyhjbGVhbl90ZXN0X2RhdGFfZXopID09IFRdCmNsZWFuX3Rlc3RfZGF0YV9leiA9IHRyYW5zZm9ybV9yZW1vdmVfc2tldyhjbGVhbl90ZXN0X2RhdGFfZXosIG51bWVyaWNfY29scykKYGBgCgpEcm9wIHN1YmplY3QgaWRlbnRpZmllciBjb2x1bW4sIG1lYW4gaW1wdXRlIGFuZCBkcm9wIGNvbHMgd2l0aCBubyB2YXJpYW5jZQoKYGBge3J9CmNsZWFuX3Rlc3RfZGF0YV9lel9zdGQgPSBjbGVhbl90ZXN0X2RhdGFfZXogJT4lIG11dGF0ZV9pZihpcy5udW1lcmljLCBzY2FsZSkKCiNtZWFuIGltcHV0YXRpb24KY2xlYW5fdGVzdF9kYXRhX2V6X3N0ZFtpcy5uYShjbGVhbl90ZXN0X2RhdGFfZXpfc3RkKV09MAoKI2Ryb3AgY29scyB3aXRoIG5vIHZhcmlhbmNlCmNsZWFuX3Rlc3RfZGF0YV9lel9zdGQgPSBjbGVhbl90ZXN0X2RhdGFfZXpfc3RkICU+JQogIHNlbGVjdF9pZihmdW5jdGlvbihjb2wpIHNkKGNvbCkgIT0gMCkKYGBgCgpSZXNpZHVhbGl6ZSBBZ2UgYW5kIFNleCBlZmZlY3RzIAoKYGBge3J9CmRhdGFfcGF0aCA9ICcvVXNlcnMvemV5bmVwZW5rYXZpL0RvY3VtZW50cy9Qb2xkcmFja0xhYkxvY2FsL1NlbGZfUmVndWxhdGlvbl9PbnRvbG9neS9EYXRhLycKcmVsZWFzZSA9ICdDb21wbGV0ZV8wMy0yOS0yMDE4LycKZGF0YXNldCA9ICdkZW1vZ3JhcGhpY19oZWFsdGguY3N2JwoKZGVtb2dyYXBoaWNzID0gZ2V0X2RlbW9ncmFwaGljcyhkYXRhc2V0ID0gcGFzdGUwKGRhdGFfcGF0aCwgcmVsZWFzZSwgZGF0YXNldCkpCgpkZW1vZ3JhcGhpY3MgPSBkZW1vZ3JhcGhpY3MgJT4lCiAgZmlsdGVyKFggJWluJSB0ZXN0X2RhdGEkc3ViX2lkKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmNsZWFuX3Rlc3RfZGF0YV9lel9zdGQgPSBjYmluZChjbGVhbl90ZXN0X2RhdGFfZXpfc3RkLCBkZW1vZ3JhcGhpY3NbLGMoIkFnZSIsICJTZXgiKV0pCgpyZXNfY2xlYW5fdGVzdF9kYXRhX2V6ID0gcmVzaWR1YWxpemVfYmFzZWxpbmUoY2xlYW5fdGVzdF9kYXRhX2V6X3N0ZCkKYGBgCgojIyBQQ0Egb24gRVogdmFyaWFibGVzIG9mIHRlc3QgZGF0YQoKIyMjIE51bWJlciBvZiB2YXJpYWJsZXMgYW5hbHlzaXMKCkluIHRoaXMgZGF0YXNldCB0aGVyZSBhcmUgbW9yZSB2YXJpYWJsZXMgdGhhbiBvYnNlcnZhdGlvbnMuIAoKR2l2ZW4gdGhlIGxhcmdlIG51bWJlciBvZiBtZWFzdXJlcyBzb21lIGNhbiBwb3NzaWJsZSBiZSByZXByZXNlbnRlZCBhcyBsaW5lYXIgY29tYmluYXRpb25zIGFzIG90aGVycy4gIAoKVGhpcyB3b3VsZCByZXN1bHQgaW4gMCBvciBuZWdhdGl2ZSB2YWx1ZXMgaW4gdGhlIGVpZ2VudmFsdWVzIG9mIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggYW5kIHdvdWxkIG1lYW4gdGhhdCB0aGUgY29ycmVsYXRpb24gbWF0cml4IGlzIG5vdCBwb3NpdGl2ZSBkZWZpbml0ZS4gIAoKSXQgaXMgaGFyZCB0byBkZXRlY3Qgc3VjaCBtdWx0aXBsZSBkZXBlbmRlbmNpZXMuIEdpdmVuIHRoZSBudW1iZXIgb2YgcG9zc2libGUgY29tYmluYXRpb25zIG9mIHZhcmlhYmxlcyBmcm9tIGFsbCBwb3NzaWJsZSB2YXJpYWJsZXMgaXQgaXMgYWxzbyBpbXBvc3NpYmxlIHRvIGNvbXB1dGUgdGhlIGV4YWN0IGxhcmdlc3QgY29tYmluYXRpb24gb2YgdmFyaWFibGVzIHRoYXQgaGFzIGEgcG9zaXRpdmUgZGVmaW5pdGUgY29ycmVsYXRpb24gbWF0cml4LgoKU28gaW5zdGVhZCBJIHVzZWQgYSBzYW1wbGluZyBhcHByb2FjaC4gQmVsb3cgaXMgYSBwbG90IG9mIHRoZSBwcm9wb3J0aW9uIG9mIHBvc2l0aXZlIGRlZmluaXRlIG1hdHJpY2VzIG91dCBvZiAxMDAwIHNhbXBsZXMgb2YgdGhlIGdpdmVuIG51bWJlcnMgb2YgdmFyaWFibGVzIGRyYXduIGZyb20gYWxsIHZhcmlhYmxlcy4KClRoZSBwbG90IHNob3dzIHRoYXQgdGhlcmUgaXMgYSBzdGVhZHkgZGVjcmVhc2UgaW4gdGhlIHByb3BvcnRpb24gb2YgcG9zaXRpdmUgZGVmaW5pdGUgbWF0cmljZXMgdGhlIG1vcmUgdmFyaWFibGVzIGFyZSB1c2VkLiBJdCBzaGFycGx5IGRyb3BzIHRvIDAgZnJvbSBjaGFuY2UgYXQgMTYwIHZhcmlhYmxlcy4gICAKCmBgYHtyfQptYXhfbnVtX3ZhcnNfc3VtbWFyeSA9IHJlYWQuY3N2KCcvVXNlcnMvemV5bmVwZW5rYXZpL0Ryb3Bib3gvUG9sZHJhY2tMYWIvU1JPX0RETV9BbmFseXNlcy9pbnB1dC9tYXhfbnVtX3ZhcnNfc3VtbWFyeS5jc3YnKQoKcCA9IG1heF9udW1fdmFyc19zdW1tYXJ5ICU+JQogIGdncGxvdChhZXMoYXMuZmFjdG9yKGN1cl9udW1fdmFycyksIHByb3BfZGV0X3BvcykpKwogIGdlb21fcG9pbnQoKSsKICBnZW9tX2xpbmUoZ3JvdXA9MSkrCiAgeGxhYigiTnVtYmVyIG9mIHZhcmlhYmxlcyBkcmF3biIpKwogIHlsYWIoIlByb3BvcnRpb24gb2YgcG9zaXRpdmUgZGVmaW5pdGUgY29ycmVsYXRpb24gbWF0cmljZXMiKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKCmdnc2F2ZSgnbWF4X251bV92YXJzLmpwZWcnLHBsb3Q9cCwgZGV2aWNlID0gJ2pwZWcnLCBwYXRoID0gZmlnX3BhdGgsIHdpZHRoID0xMiwgaGVpZ2h0ID0gNCwgdW5pdHMgPSAiaW4iKQoKcApgYGAKClRoaXMgbWVhbnMgdGhhdCBhbnkgZWZmb3J0IHRvIHJlZHVjZSBkaW1lbnNpb25hbGl0eSB1c2luZyBhbiBlaWdlbnZhbHVlIGRlY29tcG9zaXRpb24gd2lsbCBlbmNvdW50ZXIgcHJvYmxlbXMuIAoKRm9yIG51bWVyaWNhbCBlZmZpY2llbmN5IGFuZCBzdGFiaWxpdHkgbWFueSBQQ0EgYXBwbGljYXRpb25zIHVzZSBzaW5ndWxhciB2YWx1ZSBkZWNvbXBvc2l0aW9uIGluc3RlYWQgKGUuZy4gc2VlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBgcHJpbmNvbXBgIGFuZCBgcHJjb21wYCBmdW5jdGlvbnMgaW4gdGhlIGBiYXNlYCBwYWNrYWdlKS4KClRvIGJlIGNvbnNpc3RlbnQgd2l0aCB0aGUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIG1ldGhvZHMgdXNlZCBpbiB0aGUgb250b2xvZ3kgcGFwZXIgSSB3aWxsIHVzZSB0aGUgYHBzeWNoYCBwYWNrYWdlIGZ1bmN0aW9ucyBmb3IgUENBIGFuZCBFRkEuIFRoZXNlIHJlbHkgb24gYW4gZWlnZW52YWx1ZSBkZWNvbXBvc2l0aW9uIGJ1dCBoYXZlIGEgYnVpbHQtaW4gZnVuY3Rpb24gdG8gc21vb3RoIG92ZXIgbmVnYXRpdmUgZWlnZW52YWx1ZXMgdGhhdCBwcmludHMgYSB3YXJuaW5nIGJ1dCBjb250aW51ZXMgdG8gZXhlY3V0ZSBhbmQgeWllbGQgYSBzb2x1dGlvbi4KCkJ1dCB0byBjb25maXJtIHRoYXQgdGhlIHJlc3VsdGluZyBmYWN0b3JzIGFyZSBub3QgcHJvYmxlbWF0aWMgSSB3aWxsIHJ1biBhbiBTVkQgYW5kIHJlY292ZXIgdGhlIHNhbWUgcGFyYW1ldGVycyBhcyB3ZWxsLgoKIyMjIEVpZ2VuIG9uIGNvciBtYXRyaXgKCkVpZ2VudmFsdWUgZGVjb21wb3NpdGlvbiBvbiBjb3JyZWxhdGlvbiBtYXRyaXguCgpgYGB7cn0KZXpfdDFfcGNhXzMgPSBwcmluY2lwYWwocmVzX2NsZWFuX3Rlc3RfZGF0YV9leiwgbmZhY3RvcnM9Mywgcm90YXRlPSJvYmxpbWluIikKYGBgCgpTY3JlZSBwbG90IG9mIGZpcnN0IDEwIGNvbXBvbmVudHMKCmBgYHtyfQpkYXRhLmZyYW1lKGV6X3QxX3BjYV8zJHZhbHVlcykgJT4lCiAgcmVuYW1lKGVpZyA9IGV6X3QxX3BjYV8zLnZhbHVlcykgJT4lCiAgYXJyYW5nZSgtZWlnKSAlPiUKICBtdXRhdGUodmFyX3BjdCA9IGVpZy9zdW0oZWlnKSoxMDAsCiAgICAgICAgIHBjID0gMTpuKCkpICU+JQogIGZpbHRlcihwYzwxMSklPiUKICBnZ3Bsb3QoYWVzKGZhY3RvcihwYyksIHZhcl9wY3QpKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKwogIHlsYWIoIlBlcmNlbnRhZ2Ugb2YgdmFyaWFuY2UgZXhwbGFpbmVkIikrCiAgeGxhYigiUHJpbmNpcGFsIGNvbXBvbmVudCIpCmBgYAoKRGlmZmVyZW5jZSBpbiBwZXJjZW50YWdlIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBpcyBiZWxvdyAxJSBhZnRlciB0aGUgMyBjb21wb25lbnQuCgpgYGB7cn0KZGF0YS5mcmFtZShlel90MV9wY2FfMyR2YWx1ZXMpICU+JQogIHJlbmFtZShlaWcgPSBlel90MV9wY2FfMy52YWx1ZXMpICU+JQogIGFycmFuZ2UoLWVpZykgJT4lCiAgbXV0YXRlKHZhcl9wY3QgPSBlaWcvc3VtKGVpZykqMTAwLAogICAgICAgICBwYyA9IDE6bigpLAogICAgICAgICB2YXJfcGN0X3NoaWZ0ID0gbGVhZCh2YXJfcGN0KSwKICAgICAgICAgdmFyX3BjdF9kaWZmID0gdmFyX3BjdCAtIHZhcl9wY3Rfc2hpZnQpCmBgYAoKVmlzdWFsaXppbmcgdGhlIGZpcnN0IHRocmVlIGNvcG9uZW50cyBhbmQgY29sb3JpbmcgdGhlbSBieSB0aGUgcGFyYW1ldGVyIHR5cGUuCgpgYGB7cn0KZXpfdDFfcGNhXzNfbG9hZGluZ3MgPSBhcy5kYXRhLmZyYW1lKGV6X3QxX3BjYV8zJGxvYWRpbmdzW10pCgplel90MV9wY2FfM19sb2FkaW5nc1thYnMoZXpfdDFfcGNhXzNfbG9hZGluZ3MpPDAuM109TkEKCnRtcCA9IGV6X3QxX3BjYV8zX2xvYWRpbmdzICU+JQogIG11dGF0ZShkdiA9IHJvdy5uYW1lcyguKSkgJT4lCiAgc2VsZWN0KGR2LCBUQzEsIFRDMiwgVEMzKSAlPiUKICBtdXRhdGUobnVtX2xvYWRpbmcgPSAzLShpcy5uYShUQzEpK2lzLm5hKFRDMikraXMubmEoVEMzKSApICkgJT4lCiAgZmlsdGVyKG51bV9sb2FkaW5nIT0wKSAlPiUKICBzZWxlY3QoLW51bV9sb2FkaW5nKSAlPiUKICBhcnJhbmdlKC1UQzEsIC1UQzIsIC1UQzMpICU+JQogIG11dGF0ZShvcmRlcl9udW0gPSAxOm4oKSwKICAgICAgICAgZHYgPSByZW9yZGVyKGR2LCAtb3JkZXJfbnVtKSkgJT4lCiAgc2VsZWN0KC1vcmRlcl9udW0pICU+JQogIGdhdGhlcihGYWN0b3IsIExvYWRpbmcsIC1kdikgJT4lCiAgbmEuZXhjbHVkZSgpICU+JQogIG11dGF0ZShuZWdfbG9hZCA9IGZhY3RvcihpZmVsc2UoTG9hZGluZz4wLCJOQSIsIiMwMDAwMDAiKSksCiAgICAgICAgIHZhcl90eXBlID0gaWZlbHNlKGdyZXBsKCJkcmlmdCIsIGR2KSwgImRyaWYgcmF0ZSIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgidGhyZXNoIiwgZHYpLCAidGhyZXNob2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgibm9uX2RlYyIsIGR2KSwgIm5vbi1kZWNpc2lvbiIsIE5BKSkpKQoKcCA9IHRtcCU+JSAgICAgICAgIAogIGdncGxvdChhZXMoZHYsIGFicyhMb2FkaW5nKSwgZmlsbD12YXJfdHlwZSwgY29sID0gbmVnX2xvYWQpKSsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgZmFjZXRfd3JhcCh+RmFjdG9yLCBucm93PTEpKwogIGNvb3JkX2ZsaXAoKSsKICB4bGFiKCIiKSsKICB5bGFiKCJBYnNvbHV0ZSBMb2FkaW5nIikrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNiYlBhbGV0dGUpKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKCmdnc2F2ZSgnRVpfUENBX1QxXzMuanBlZycscGxvdD1wLCBkZXZpY2UgPSAnanBlZycsIHBhdGggPSBmaWdfcGF0aCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIsIHVuaXRzID0gImluIikKCnAKYGBgCgojIyMgU1ZECgpEb3VibGUgY2hlY2tpbmcgdGhlIGFib3ZlIGZhY3RvciBzb2x1dGlvbiB1c2luZyBhIHNpbmd1bGFyIHZhbHVlIGRlY29tcG9zaXRpb24gYW5kIG9ibGltaW4gcm90YXRpb24uCgpgYGB7cn0KZXpfc3ZkIDwtIHN2ZChyZXNfY2xlYW5fdGVzdF9kYXRhX2V6KQpuY29tcCA9IDMKZGYgPC0gbnJvdyhyZXNfY2xlYW5fdGVzdF9kYXRhX2V6KSAtIDEKZXpfdDFfc3ZkX3Jhd0xvYWRpbmdzID0gZXpfc3ZkJHZbLDE6bmNvbXBdICUqJSBkaWFnKGV6X3N2ZCRkL3NxcnQoZGYpLCBuY29tcCwgbmNvbXApCmV6X3QxX3N2ZF9yb3RhdGVkTG9hZGluZ3MgPC0gR1BBcm90YXRpb246Om9ibGltaW4oZXpfdDFfc3ZkX3Jhd0xvYWRpbmdzKSRsb2FkaW5ncwpgYGAKCmBgYHtyfQplel90MV9zdmRfcm90YXRlZExvYWRpbmdzW2Ficyhlel90MV9zdmRfcm90YXRlZExvYWRpbmdzKTwwLjNdPU5BCgp0bXAgPSBkYXRhLmZyYW1lKGV6X3QxX3N2ZF9yb3RhdGVkTG9hZGluZ3MpICU+JQogIG11dGF0ZShkdiA9IG5hbWVzKHJlc19jbGVhbl90ZXN0X2RhdGFfZXopLAogICAgICAgICBYMSA9IC0xKlgxLAogICAgICAgICBYMz0gLTEqWDMpICU+JQogIHNlbGVjdChkdiwgWDEsIFgyLCBYMyklPiUKICBtdXRhdGUobnVtX2xvYWRpbmcgPSAzLShpcy5uYShYMSkraXMubmEoWDIpK2lzLm5hKFgzKSApICkgJT4lCiAgZmlsdGVyKG51bV9sb2FkaW5nIT0wKSAlPiUKICBzZWxlY3QoLW51bV9sb2FkaW5nKSAlPiUKICBhcnJhbmdlKC1YMSwgLVgyLCAtWDMpICU+JQogIG11dGF0ZShvcmRlcl9udW0gPSAxOm4oKSwKICAgICAgICAgZHYgPSByZW9yZGVyKGR2LCAtb3JkZXJfbnVtKSkgJT4lCiAgc2VsZWN0KC1vcmRlcl9udW0pICU+JQogIGdhdGhlcihGYWN0b3IsIExvYWRpbmcsIC1kdikgJT4lCiAgbmEuZXhjbHVkZSgpICU+JQogIG11dGF0ZShuZWdfbG9hZCA9IGZhY3RvcihpZmVsc2UoTG9hZGluZz4wLCJOQSIsIiMwMDAwMDAiKSksCiAgICAgICAgIHZhcl90eXBlID0gaWZlbHNlKGdyZXBsKCJkcmlmdCIsIGR2KSwgImRyaWYgcmF0ZSIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgidGhyZXNoIiwgZHYpLCAidGhyZXNob2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgibm9uX2RlYyIsIGR2KSwgIm5vbi1kZWNpc2lvbiIsIE5BKSkpKQoKcCA9IHRtcCU+JSAgICAgICAgIAogIGdncGxvdChhZXMoZHYsIGFicyhMb2FkaW5nKSwgZmlsbD12YXJfdHlwZSwgY29sID0gbmVnX2xvYWQpKSsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgZmFjZXRfd3JhcCh+RmFjdG9yLCBucm93PTEpKwogIGNvb3JkX2ZsaXAoKSsKICB4bGFiKCIiKSsKICB5bGFiKCJBYnNvbHV0ZSBMb2FkaW5nIikrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNiYlBhbGV0dGUpKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKCmdnc2F2ZSgnRVpfU1ZEX1QxXzMuanBlZycscGxvdD1wLCBkZXZpY2UgPSAnanBlZycsIHBhdGggPSBmaWdfcGF0aCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIsIHVuaXRzID0gImluIikKCnAKYGBgCgojIyBFRkEgb24gRVogdmFyaWFibGVzIG9mIHRlc3QgZGF0YQoKSG93IG1hbnkgZmFjdG9ycyBzaG91bGQgYmUgZXh0cmFjdGVkIGZyb20gdGhlIEVGQT8gVG8gYW5zd2VyIHRoaXMgd2UgcnVuIG1vZGVscyBleHRyYWN0aW5nIDIgdG8gNTAgY29tcG9uZW50cyBhbmQgcmFuayB0aGVtIGJ5IEJJQy4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmVmYV9lel90MV9jb21wX21ldHJpY3MgPSBmaW5kX29wdGltYWxfY29tcG9uZW50cyhyZXNfY2xlYW5fdGVzdF9kYXRhX2V6LCBmbSA9ICJtaW5yZXMiLCBtaW5jPTIpCmBgYAoKVGhpcyBzdWdnZXN0cyB0aGF0IGEgMTYgY29tcG9uZW50IHNvbHV0aW9uIHdvdWxkIGJlIHRoZSBiZXN0IGZpdHRpbmcgbW9kZWwuCgpgYGB7cn0KZWZhX2V6X3QxX2NvbXBfbWV0cmljcwpgYGAKCkZpdCB0aGUgbW9kZWwgc3VnZ2VzdGVkIGJ5IHRoZSBCSUMgY29tcGFyaXNvbi4KCmBgYHtyfQplel90MV9mYV8xNiA9IGZhKHJlc19jbGVhbl90ZXN0X2RhdGFfZXosIGVmYV9lel90MV9jb21wX21ldHJpY3MkY29tcFsxXSwgcm90YXRlPSdvYmxpbWluJywgZm09J21pbnJlcycsIHNjb3Jlcz0ndGVuQmVyZ2UnKQpgYGAKCkZpdCB0aGUgMyBmYWN0b3IgbW9kZWwgdGhhdCBpcyBvZiB0aGVvcmV0aWNhbCBpbnRlcmVzdC4KCmBgYHtyfQplel90MV9mYV8zID0gZmEocmVzX2NsZWFuX3Rlc3RfZGF0YV9leiwgMywgcm90YXRlPSdvYmxpbWluJywgZm09J21pbnJlcycsIHNjb3Jlcz0ndGVuQmVyZ2UnKQpgYGAKCkRpZmZlcmVuY2VzIGluIEJJQydzIGFyZSBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0LiAKCkkgdHJpZWQgdG8gcnVuIGEgbW9yZSBmb3JtYWwgbW9kZWwgY29tcGFyaXNvbiBiZXR3ZWVuIHRoZSAzIGZhY3RvciBzb2x1dGlvbiB0aGF0IHdlIGhhdmUgdGhlb3JldGljYWwgcmVhc29ucyBmb3IgYW5kIHRoZSAxNiBmYWN0b3IgbW9kZWwgdGhhdCBpcyBzZWxlY3RlZCBiYXNlZCBvbiBCSUMuIEJlbG93IGlzIHRoZSByZXN1bHQuIEknbSBub3QgcXVpZXQgc3VyZSBob3cgdG8gaW50ZXJwcmV0ZWQgYnV0IGl0IHNlZW1zIHRvIHN1Z2dlc3QgdGhhdCB0aGUgbW9yZSBjb21wbGljYXRlZCBtb2RlbCBpcyBub3Qgc2lnbmlmaWNhbnRseSBiZXR0ZXIuCgpgYGB7cn0KYW5vdmEoZXpfdDFfZmFfMywgZXpfdDFfZmFfMTYpCmBgYAoKU28gd2hhdCB3b3VsZCB0aGUgMyBmYWN0b3Igc29sdXRpb24gbG9vayBsaWtlPyBOb3Qgc3VycHJpc2luZ2x5LCB2ZXJ5IHNpbWlsYXIgdG8gdGhlIFBDJ3MuCgpgYGB7cn0KZXpfdDFfZmFfM19sb2FkaW5ncyA9IGFzLmRhdGEuZnJhbWUoZXpfdDFfZmFfMyRsb2FkaW5nc1tdKQoKZXpfdDFfZmFfM19sb2FkaW5nc1thYnMoZXpfdDFfZmFfM19sb2FkaW5ncyk8MC4zXT1OQQoKdG1wID0gZXpfdDFfZmFfM19sb2FkaW5ncyAlPiUKICBtdXRhdGUoZHYgPSByb3cubmFtZXMoLikpICU+JQogIHNlbGVjdChkdiwgTVIxLCBNUjIsIE1SMykgJT4lCiAgbXV0YXRlKG51bV9sb2FkaW5nID0gMy0oaXMubmEoTVIxKStpcy5uYShNUjIpK2lzLm5hKE1SMykgKSApICU+JQogIGZpbHRlcihudW1fbG9hZGluZyE9MCkgJT4lCiAgc2VsZWN0KC1udW1fbG9hZGluZykgJT4lCiAgYXJyYW5nZSgtTVIxLCAtTVIyLCAtTVIzKSAlPiUKICBtdXRhdGUob3JkZXJfbnVtID0gMTpuKCksCiAgICAgICAgIGR2ID0gcmVvcmRlcihkdiwgLW9yZGVyX251bSkpICU+JQogIHNlbGVjdCgtb3JkZXJfbnVtKSAlPiUKICBnYXRoZXIoRmFjdG9yLCBMb2FkaW5nLCAtZHYpICU+JQogIG5hLmV4Y2x1ZGUoKSAlPiUKICBtdXRhdGUobmVnX2xvYWQgPSBmYWN0b3IoaWZlbHNlKExvYWRpbmc+MCwiTkEiLCIjMDAwMDAwIikpLAogICAgICAgICB2YXJfdHlwZSA9IGlmZWxzZShncmVwbCgiZHJpZnQiLCBkdiksICJkcmlmIHJhdGUiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoInRocmVzaCIsIGR2KSwgInRocmVzaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIm5vbl9kZWMiLCBkdiksICJub24tZGVjaXNpb24iLCBOQSkpKSkKCiAgICAgCnAgPSB0bXAlPiUgICAgICAgICAKICBnZ3Bsb3QoYWVzKGR2LCBhYnMoTG9hZGluZyksIGZpbGw9dmFyX3R5cGUsIGNvbCA9IG5lZ19sb2FkKSkrCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpKwogIGZhY2V0X3dyYXAofkZhY3RvciwgbnJvdz0xKSsKICBjb29yZF9mbGlwKCkrCiAgeGxhYigiIikrCiAgeWxhYigiQWJzb2x1dGUgTG9hZGluZyIpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jYmJQYWxldHRlKSsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoJ0VaX0ZBX1QxXzMuanBlZycscGxvdD1wLCBkZXZpY2UgPSAnanBlZycsIHBhdGggPSBmaWdfcGF0aCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIsIHVuaXRzID0gImluIikKCnAKYGBgCgpBcmUgdGhlcmUgbm90YWJsZSBkaWZmZXJlbmNlcyBpbiB0aGUgMTYgZmFjdG9yIHNvbHV0aW9uPyAoTG9va2luZyBhdCB0aGUgZmlyc3QgaGFsZiBvZiB0aGUgbW9kZWwpIFRoZSB0aHJlZSBwYXJhbWV0ZXIgdHlwZXMgc2VlbSB0byBsb2FkIG9uIHR3byBmYWN0b3JzIGVhY2ggaW5zdGVhZCBvZiBvbmUuIFRoZXNlIHRlbmQgdG8gcmVmbGVjdCBzZXBhcmF0aW9ucyBiZXR3ZWVuIHRhc2tzIChlLmcuIG9uZSBub24tZGVjaXNpb24gZmFjdG9yIGlzIGp1c3QgZm9yIHRoZSBzaGFwZSBtYXRjaGluZyB0YXNrIHdoaWxlIHRoZSBvdGhlciBjb250YWlucyBhbGwgdGhlIHJlbWFpbmluZyBvbmVzKS4gU2luY2UgdGhlc2UgZGlmZmVyZW5jZXMgYXJlIG9mIGxlc3MgdGhlb3JldGljYWwgaW50ZXJlc3QgZm9yIHRoaXMgZXhlcmNpc2UgSSB0aGluayBjaG9vc2luZyB0aGUgdGhyZWUgZmFjdG9yIHNvbHV0aW9uIHdvdWxkIGJlIGFwcHJvcHJpYXRlLgoKYGBge3J9CmV6X3QxX2ZhXzE2X2xvYWRpbmdzID0gYXMuZGF0YS5mcmFtZShlel90MV9mYV8xNiRsb2FkaW5nc1tdKQoKZXpfdDFfZmFfMTZfbG9hZGluZ3NbYWJzKGV6X3QxX2ZhXzE2X2xvYWRpbmdzKTwwLjNdPU5BCgp0bXAgPSBlel90MV9mYV84X2xvYWRpbmdzICU+JQogIG11dGF0ZShkdiA9IHJvdy5uYW1lcyguKSkgJT4lCiAgc2VsZWN0KGR2LCBNUjEsIE1SMiwgTVIzLCBNUjQsIE1SNSwgTVI2LCBNUjcsIE1SOCkgJT4lCiAgbXV0YXRlKG51bV9sb2FkaW5nID0gOC0oaXMubmEoTVIxKStpcy5uYShNUjIpK2lzLm5hKE1SMykraXMubmEoTVI0KStpcy5uYShNUjUpK2lzLm5hKE1SNikraXMubmEoTVI3KStpcy5uYShNUjgpICkgKSAlPiUKICBmaWx0ZXIobnVtX2xvYWRpbmchPTApICU+JQogIHNlbGVjdCgtbnVtX2xvYWRpbmcpICU+JQogIGFycmFuZ2UoLU1SMSwgLU1SMiwgLU1SMywtTVIxLCAtTVI1LCAtTVI2LC1NUjcsIC1NUjgpICU+JQogIG11dGF0ZShvcmRlcl9udW0gPSAxOm4oKSwKICAgICAgICAgZHYgPSByZW9yZGVyKGR2LCAtb3JkZXJfbnVtKSkgJT4lCiAgc2VsZWN0KC1vcmRlcl9udW0pICU+JQogIGdhdGhlcihGYWN0b3IsIExvYWRpbmcsIC1kdikgJT4lCiAgbmEuZXhjbHVkZSgpICU+JQogIG11dGF0ZShuZWdfbG9hZCA9IGZhY3RvcihpZmVsc2UoTG9hZGluZz4wLCJOQSIsIiMwMDAwMDAiKSksCiAgICAgICAgIHZhcl90eXBlID0gaWZlbHNlKGdyZXBsKCJkcmlmdCIsIGR2KSwgImRyaWYgcmF0ZSIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgidGhyZXNoIiwgZHYpLCAidGhyZXNob2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgibm9uX2RlYyIsIGR2KSwgIm5vbi1kZWNpc2lvbiIsIE5BKSkpKQpwID0gdG1wJT4lICAgICAgICAgCiAgZ2dwbG90KGFlcyhkdiwgYWJzKExvYWRpbmcpLCBmaWxsPXZhcl90eXBlLCBjb2wgPSBuZWdfbG9hZCkpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICBmYWNldF93cmFwKH5GYWN0b3IsIG5yb3c9MSkrCiAgY29vcmRfZmxpcCgpKwogIHhsYWIoIiIpKwogIHlsYWIoIkFic29sdXRlIExvYWRpbmciKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2JiUGFsZXR0ZSkrCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKQogICAgICAgICNheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKCmdnc2F2ZSgnRVpfRkFfVDFfOC5qcGVnJyxwbG90PXAsIGRldmljZSA9ICdqcGVnJywgcGF0aCA9IGZpZ19wYXRoLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMiwgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoZmlnX3BhdGgsICdFWl9GQV9UMV84LmpwZWcnKSkKYGBgCgpCYXNlZCBvbiB0aGVzZSByZXN1bHRzIEkgcmFuIDMgZmFjdG9yIEVGQSdzIG9uIHRoZSByZW1haW5pbmcgZGF0YQoKIyMgRUZBIG9uIEhERE0gdmFyaWFibGVzIG9mIHRlc3QgZGF0YQoKRXh0cmFjdCBFWiB2YXJpYWJsZXMKCmBgYHtyfQp0ZXN0X2RhdGFfaGRkbSA9IHRlc3RfZGF0YSAlPiUKICBzZWxlY3QoZ3JlcCgnaGRkbScsIG5hbWVzKHRlc3RfZGF0YSksIHZhbHVlPVQpKQpgYGAKClJlbW92ZSB2YXJpYWJsZXMgdGhhdCBhcmUgY29ycmVsYXRlZCA+MC44NQoKYGBge3J9CmNsZWFuX3Rlc3RfZGF0YV9oZGRtID0gcmVtb3ZlX2NvcnJlbGF0ZWRfdGFza192YXJpYWJsZXModGVzdF9kYXRhX2hkZG0pCmBgYAoKUmVtb3ZlIG91dGxpZXJzICg+Mi41IFNEIGF3YXkpCgpgYGB7cn0KY2xlYW5fdGVzdF9kYXRhX2hkZG0gPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGNsZWFuX3Rlc3RfZGF0YV9oZGRtLCAyLCByZW1vdmVfb3V0bGllcnMpKQpgYGAKClRyYW5zZm9ybSBza2V3ZWQgdmFyaWFibGVzCgpgYGB7cn0KI05vdGhpbmcgdG8gdHJhbnNmb3JtCm51bWVyaWNfY29scyA9IGdldF9udW1lcmljX2NvbHMoKQpudW1lcmljX2NvbHMgPSBudW1lcmljX2NvbHNbbnVtZXJpY19jb2xzICVpbiUgbmFtZXMoY2xlYW5fdGVzdF9kYXRhX2hkZG0pID09IFRdCmNsZWFuX3Rlc3RfZGF0YV9oZGRtID0gdHJhbnNmb3JtX3JlbW92ZV9za2V3KGNsZWFuX3Rlc3RfZGF0YV9oZGRtLCBudW1lcmljX2NvbHMpCmBgYAoKRHJvcCBzdWJqZWN0IGlkZW50aWZpZXIgY29sdW1uLCBtZWFuIGltcHV0ZSBhbmQgZHJvcCBjb2xzIHdpdGggbm8gdmFyaWFuY2UKCmBgYHtyfQpjbGVhbl90ZXN0X2RhdGFfaGRkbV9zdGQgPSBjbGVhbl90ZXN0X2RhdGFfaGRkbSAlPiUgbXV0YXRlX2lmKGlzLm51bWVyaWMsIHNjYWxlKQoKI21lYW4gaW1wdXRhdGlvbgpjbGVhbl90ZXN0X2RhdGFfaGRkbV9zdGRbaXMubmEoY2xlYW5fdGVzdF9kYXRhX2hkZG1fc3RkKV09MAoKI2Ryb3AgY29scyB3aXRoIG5vIHZhcmlhbmNlCmNsZWFuX3Rlc3RfZGF0YV9oZGRtX3N0ZCA9IGNsZWFuX3Rlc3RfZGF0YV9oZGRtX3N0ZCAlPiUKICBzZWxlY3RfaWYoZnVuY3Rpb24oY29sKSBzZChjb2wpICE9IDApCmBgYAoKUmVzaWR1YWxpemUgQWdlIGFuZCBTZXggZWZmZWN0cyAKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmNsZWFuX3Rlc3RfZGF0YV9oZGRtX3N0ZCA9IGNiaW5kKGNsZWFuX3Rlc3RfZGF0YV9oZGRtX3N0ZCwgZGVtb2dyYXBoaWNzWyxjKCJBZ2UiLCAiU2V4IildKQoKcmVzX2NsZWFuX3Rlc3RfZGF0YV9oZGRtID0gcmVzaWR1YWxpemVfYmFzZWxpbmUoY2xlYW5fdGVzdF9kYXRhX2hkZG1fc3RkKQpgYGAKCkZpdCB0aGUgMyBmYWN0b3IgbW9kZWwgdGhhdCBpcyBvZiB0aGVvcmV0aWNhbCBpbnRlcmVzdC4KCmBgYHtyfQpoZGRtX3QxX2ZhXzMgPSBmYShyZXNfY2xlYW5fdGVzdF9kYXRhX2hkZG0sIDMsIHJvdGF0ZT0nb2JsaW1pbicsIGZtPSdtaW5yZXMnLCBzY29yZXM9J3RlbkJlcmdlJykKYGBgCgpUaGUgdGhyZWUgZmFjdG9yIG1vZGVsIGRvZXMgbm90IHdvcmsgYXMgd2VsbCBmb3IgSERETSdzIGJlY2F1c2Ugb2YgdGhlIDEwNiB2YXJpYWJsZXMgd2UgaGF2ZSA3NSBhcmUgZHJpZnQgcmF0ZXMsIDE3IGFyZSB0aHJlc2hvbGQgYW5kIDE0IGFyZSBub24tZGVjaXNpb24gdGltZXMuIFRoaXMgaXMgYmVjYXVzZSB3aGlsZSBhbGwgdGhlIHBhcmFtZXRlcnMgYXJlIGZpdCBzZXBhcmF0ZWx5IGZvciBFWiBvbmx5IHRoZSBkcmlmdCByYXRlIHBhcmFtZXRlciBpcyBhbGxvd2VkIHRvIHZhcnkgYnkgY29uZGl0aW9uIGZvciB0aGUgSERETS4gU28gdGhlIG1vZGVsIGlzIG1vc3RseSBzZXBhcmF0aW5nIG91dCBkcmlmdCByYXRlcyBmcm9tIGVhY2ggb3RoZXIuCgpgYGB7cn0KaGRkbV90MV9mYV8zX2xvYWRpbmdzID0gYXMuZGF0YS5mcmFtZShoZGRtX3QxX2ZhXzMkbG9hZGluZ3NbXSkKCmhkZG1fdDFfZmFfM19sb2FkaW5nc1thYnMoaGRkbV90MV9mYV8zX2xvYWRpbmdzKTwwLjNdPU5BCgp0bXAgPSBoZGRtX3QxX2ZhXzNfbG9hZGluZ3MgJT4lCiAgbXV0YXRlKGR2ID0gcm93Lm5hbWVzKC4pKSAlPiUKICBzZWxlY3QoZHYsIE1SMSwgTVIyLCBNUjMpICU+JQogIG11dGF0ZShudW1fbG9hZGluZyA9IDMtKGlzLm5hKE1SMSkraXMubmEoTVIyKStpcy5uYShNUjMpICkgKSAlPiUKICBmaWx0ZXIobnVtX2xvYWRpbmchPTApICU+JQogIHNlbGVjdCgtbnVtX2xvYWRpbmcpICU+JQogIGFycmFuZ2UoLU1SMSwgLU1SMiwgLU1SMykgJT4lCiAgbXV0YXRlKG9yZGVyX251bSA9IDE6bigpLAogICAgICAgICBkdiA9IHJlb3JkZXIoZHYsIC1vcmRlcl9udW0pKSAlPiUKICBzZWxlY3QoLW9yZGVyX251bSkgJT4lCiAgZ2F0aGVyKEZhY3RvciwgTG9hZGluZywgLWR2KSAlPiUKICBuYS5leGNsdWRlKCkgJT4lCiAgbXV0YXRlKG5lZ19sb2FkID0gZmFjdG9yKGlmZWxzZShMb2FkaW5nPjAsIk5BIiwiIzAwMDAwMCIpKSwKICAgICAgICAgdmFyX3R5cGUgPSBpZmVsc2UoZ3JlcGwoImRyaWZ0IiwgZHYpLCAiZHJpZiByYXRlIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJ0aHJlc2giLCBkdiksICJ0aHJlc2hvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJub25fZGVjIiwgZHYpLCAibm9uLWRlY2lzaW9uIiwgTkEpKSkpCgogICAgIApwID0gdG1wJT4lICAgICAgICAgCiAgZ2dwbG90KGFlcyhkdiwgYWJzKExvYWRpbmcpLCBmaWxsPXZhcl90eXBlLCBjb2wgPSBuZWdfbG9hZCkpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICBmYWNldF93cmFwKH5GYWN0b3IsIG5yb3c9MSkrCiAgY29vcmRfZmxpcCgpKwogIHhsYWIoIiIpKwogIHlsYWIoIkFic29sdXRlIExvYWRpbmciKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2JiUGFsZXR0ZSkrCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQoKZ2dzYXZlKCdIRERNX1QxX0ZBXzMuanBlZycscGxvdD1wLCBkZXZpY2UgPSAnanBlZycsIHBhdGggPSBmaWdfcGF0aCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIsIHVuaXRzID0gImluIikKCnAKYGBgCgpEb2VzIGl0IGZpdCBiZXR0ZXIgaWYgSSB1c2UganVzdCB0aGUgbWFpbiBkcmlmdCByYXRlcyBhbmQgbm90IHRoZSBjb25kaXRpb24gb25lcz8KCmBgYHtyfQpoZGRtX3Zhcl9zdWJzZXQgPSBjKCJhZGFwdGl2ZV9uX2JhY2suaGRkbV9kcmlmdCIgLCAiYXR0ZW50aW9uX25ldHdvcmtfdGFzay5oZGRtX2RyaWZ0IiwgImNob2ljZV9yZWFjdGlvbl90aW1lLmhkZG1fZHJpZnQiLCAiZGlyZWN0ZWRfZm9yZ2V0dGluZy5oZGRtX2RyaWZ0IiwgImRvdF9wYXR0ZXJuX2V4cGVjdGFuY3kuaGRkbV9kcmlmdCIgLCAibG9jYWxfZ2xvYmFsX2xldHRlci5oZGRtX2RyaWZ0IiwgIm1vdG9yX3NlbGVjdGl2ZV9zdG9wX3NpZ25hbC5oZGRtX2RyaWZ0IiwgICJyZWNlbnRfcHJvYmVzLmhkZG1fZHJpZnQiLCAic2hhcGVfbWF0Y2hpbmcuaGRkbV9kcmlmdCIgLCAic2ltb24uaGRkbV9kcmlmdCIsICJzdGltX3NlbGVjdGl2ZV9zdG9wX3NpZ25hbC5oZGRtX2RyaWZ0IiwgInN0b3Bfc2lnbmFsLmhkZG1fZHJpZnQiLCAic3Ryb29wLmhkZG1fZHJpZnQiICwgInRocmVlYnl0d28uaGRkbV9kcmlmdCIsICJhZGFwdGl2ZV9uX2JhY2suaGRkbV90aHJlc2giLCAiYXR0ZW50aW9uX25ldHdvcmtfdGFzay5oZGRtX3RocmVzaCIsICJjaG9pY2VfcmVhY3Rpb25fdGltZS5oZGRtX3RocmVzaCIsICJkaXJlY3RlZF9mb3JnZXR0aW5nLmhkZG1fdGhyZXNoIiwgImRvdF9wYXR0ZXJuX2V4cGVjdGFuY3kuaGRkbV90aHJlc2gubG9nVHIiLCAibG9jYWxfZ2xvYmFsX2xldHRlci5oZGRtX3RocmVzaCIsICJtb3Rvcl9zZWxlY3RpdmVfc3RvcF9zaWduYWwuaGRkbV90aHJlc2giLCAicmVjZW50X3Byb2Jlcy5oZGRtX3RocmVzaCIsICJzaGFwZV9tYXRjaGluZy5oZGRtX3RocmVzaCIsICJzaW1vbi5oZGRtX3RocmVzaCIsICJzdGltX3NlbGVjdGl2ZV9zdG9wX3NpZ25hbC5oZGRtX3RocmVzaC5sb2dUciIsICJzdG9wX3NpZ25hbC5oZGRtX3RocmVzaCIsICJzdG9wX3NpZ25hbC5oZGRtX3RocmVzaF9oaWdoIiwgInN0b3Bfc2lnbmFsLmhkZG1fdGhyZXNoX2xvdyIsICJzdG9wX3NpZ25hbC5wcm9hY3RpdmVfc2xvd2luZ19oZGRtX3RocmVzaCIsICJzdHJvb3AuaGRkbV90aHJlc2giLCAidGhyZWVieXR3by5oZGRtX3RocmVzaCIsIGdyZXAoIm5vbl9kZWMiLCBuYW1lcyh0ZXN0X2RhdGFfaGRkbSksIHZhbHVlID0gVCkpCgpyZXNfY2xlYW5fdGVzdF9kYXRhX2hkZG1fc3Vic2V0ID0gcmVzX2NsZWFuX3Rlc3RfZGF0YV9oZGRtICU+JSBzZWxlY3QoaGRkbV92YXJfc3Vic2V0KQpgYGAKCmBgYHtyfQpoZGRtX3QxX2ZhXzNfc3Vic2V0ID0gZmEocmVzX2NsZWFuX3Rlc3RfZGF0YV9oZGRtX3N1YnNldCwgMywgcm90YXRlPSdvYmxpbWluJywgZm09J21pbnJlcycsIHNjb3Jlcz0ndGVuQmVyZ2UnKQpgYGAKClRoZW4gaXQgd29ya3MgYmV0dGVyIGJ1dCBzdGlsbCBub3QgZG9pbmcgYSBncmVhdCBqb2Igc2VwYXJhdGluZyBvdXQgdGhyZXNob2xkcyBmcm9tIG5vbi1kZWNpc2lvbiB0aW1lcy4KCmBgYHtyfQpoZGRtX3QxX2ZhXzNfc3Vic2V0X2xvYWRpbmdzID0gYXMuZGF0YS5mcmFtZShoZGRtX3QxX2ZhXzNfc3Vic2V0JGxvYWRpbmdzW10pCgpoZGRtX3QxX2ZhXzNfc3Vic2V0X2xvYWRpbmdzW2FicyhoZGRtX3QxX2ZhXzNfc3Vic2V0X2xvYWRpbmdzKTwwLjNdPU5BCgp0bXAgPSBoZGRtX3QxX2ZhXzNfc3Vic2V0X2xvYWRpbmdzICU+JQogIG11dGF0ZShkdiA9IHJvdy5uYW1lcyguKSkgJT4lCiAgc2VsZWN0KGR2LCBNUjEsIE1SMiwgTVIzKSAlPiUKICBtdXRhdGUobnVtX2xvYWRpbmcgPSAzLShpcy5uYShNUjEpK2lzLm5hKE1SMikraXMubmEoTVIzKSApICkgJT4lCiAgZmlsdGVyKG51bV9sb2FkaW5nIT0wKSAlPiUKICBzZWxlY3QoLW51bV9sb2FkaW5nKSAlPiUKICBhcnJhbmdlKC1NUjEsIC1NUjIsIC1NUjMpICU+JQogIG11dGF0ZShvcmRlcl9udW0gPSAxOm4oKSwKICAgICAgICAgZHYgPSByZW9yZGVyKGR2LCAtb3JkZXJfbnVtKSkgJT4lCiAgc2VsZWN0KC1vcmRlcl9udW0pICU+JQogIGdhdGhlcihGYWN0b3IsIExvYWRpbmcsIC1kdikgJT4lCiAgbmEuZXhjbHVkZSgpICU+JQogIG11dGF0ZShuZWdfbG9hZCA9IGZhY3RvcihpZmVsc2UoTG9hZGluZz4wLCJOQSIsIiMwMDAwMDAiKSksCiAgICAgICAgIHZhcl90eXBlID0gaWZlbHNlKGdyZXBsKCJkcmlmdCIsIGR2KSwgImRyaWYgcmF0ZSIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgidGhyZXNoIiwgZHYpLCAidGhyZXNob2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgibm9uX2RlYyIsIGR2KSwgIm5vbi1kZWNpc2lvbiIsIE5BKSkpKQoKICAgICAKcCA9IHRtcCU+JSAgICAgICAgIAogIGdncGxvdChhZXMoZHYsIGFicyhMb2FkaW5nKSwgZmlsbD12YXJfdHlwZSwgY29sID0gbmVnX2xvYWQpKSsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgZmFjZXRfd3JhcCh+RmFjdG9yLCBucm93PTEpKwogIGNvb3JkX2ZsaXAoKSsKICB4bGFiKCIiKSsKICB5bGFiKCJBYnNvbHV0ZSBMb2FkaW5nIikrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNiYlBhbGV0dGUpKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkKICAgICAgICAjYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoJ0hERE1fVDFfRkFfM19zdWJzZXQuanBlZycscGxvdD1wLCBkZXZpY2UgPSAnanBlZycsIHBhdGggPSBmaWdfcGF0aCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIsIHVuaXRzID0gImluIikKYGBgCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoZmlnX3BhdGgsICdIRERNX1QxX0ZBXzNfc3Vic2V0LmpwZWcnKSkKYGBgCgojIyBFRkEgb24gRVogdmFyaWFibGVzIG9mIHJldGVzdCBkYXRhCgpVc2luZyB0aGUgbW9kZWwgZnJvbSB0aGUgdGVzdCBkYXRhIHRvIHByZWRpY3QgdGhlIHJldGVzdCBkYXRhLiBMYXRlciB3aWxsIHVzZSBmYWN0b3Igc2NvcmVzIGZyb20gdGhpcyB0byBjYWxjdWxhdGUgcmVsaWFiaWxpdHkgb2YgcmVkdWNlZCBkaW1lbnNpb25zLgoKYGBge3J9CnJldGVzdF9kYXRhX2V6ID0gcmV0ZXN0X2RhdGEgJT4lCiAgc2VsZWN0KGdyZXAoJ0VaJywgbmFtZXMocmV0ZXN0X2RhdGEpLCB2YWx1ZT1UKSkKYGBgCgpSZW1vdmUgdmFyaWFibGVzIHRoYXQgYXJlIGNvcnJlbGF0ZWQgPjAuODUKCmBgYHtyfQpjbGVhbl9yZXRlc3RfZGF0YV9leiA9IHJlbW92ZV9jb3JyZWxhdGVkX3Rhc2tfdmFyaWFibGVzKHJldGVzdF9kYXRhX2V6KQpgYGAKClJlbW92ZSBvdXRsaWVycyAoPjIuNSBTRCBhd2F5KQoKYGBge3J9CmNsZWFuX3JldGVzdF9kYXRhX2V6ID0gYXMuZGF0YS5mcmFtZShhcHBseShjbGVhbl9yZXRlc3RfZGF0YV9leiwgMiwgcmVtb3ZlX291dGxpZXJzKSkKYGBgCgpUcmFuc2Zvcm0gc2tld2VkIHZhcmlhYmxlcwoKYGBge3J9CiNOb3RoaW5nIHRvIHRyYW5zZm9ybQpudW1lcmljX2NvbHMgPSBnZXRfbnVtZXJpY19jb2xzKCkKbnVtZXJpY19jb2xzID0gbnVtZXJpY19jb2xzW251bWVyaWNfY29scyAlaW4lIG5hbWVzKGNsZWFuX3JldGVzdF9kYXRhX2V6KSA9PSBUXQpjbGVhbl9yZXRlc3RfZGF0YV9leiA9IHRyYW5zZm9ybV9yZW1vdmVfc2tldyhjbGVhbl9yZXRlc3RfZGF0YV9leiwgbnVtZXJpY19jb2xzKQpgYGAKCkRyb3Agc3ViamVjdCBpZGVudGlmaWVyIGNvbHVtbiwgbWVhbiBpbXB1dGUgYW5kIGRyb3AgY29scyB3aXRoIG5vIHZhcmlhbmNlCgpgYGB7cn0KY2xlYW5fcmV0ZXN0X2RhdGFfZXpfc3RkID0gY2xlYW5fcmV0ZXN0X2RhdGFfZXogJT4lIG11dGF0ZV9pZihpcy5udW1lcmljLCBzY2FsZSkKCiNtZWFuIGltcHV0YXRpb24KY2xlYW5fcmV0ZXN0X2RhdGFfZXpfc3RkW2lzLm5hKGNsZWFuX3JldGVzdF9kYXRhX2V6X3N0ZCldPTAKCiNkcm9wIGNvbHMgd2l0aCBubyB2YXJpYW5jZQpjbGVhbl9yZXRlc3RfZGF0YV9lel9zdGQgPSBjbGVhbl9yZXRlc3RfZGF0YV9lel9zdGQgJT4lCiAgc2VsZWN0X2lmKGZ1bmN0aW9uKGNvbCkgc2QoY29sKSAhPSAwKQpgYGAKClJlc2lkdWFsaXplIEFnZSBhbmQgU2V4IGVmZmVjdHMgCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpjbGVhbl9yZXRlc3RfZGF0YV9lel9zdGQgPSBjYmluZChjbGVhbl9yZXRlc3RfZGF0YV9lel9zdGQsIGRlbW9ncmFwaGljc1ssYygiQWdlIiwgIlNleCIpXSkKCnJlc19jbGVhbl9yZXRlc3RfZGF0YV9leiA9IHJlc2lkdWFsaXplX2Jhc2VsaW5lKGNsZWFuX3JldGVzdF9kYXRhX2V6X3N0ZCkKYGBgCgpQcmVkaWN0IHJldGVzdCBkYXRhIHVzaW5nIHRoZSAzIGZhY3RvciBFRkEgZnJvbSB0ZXN0IGRhdGEuCgpgYGB7cn0KZXpfdDJfZmFfMyA9IHByZWRpY3QoZXpfdDFfZmFfMywgcmVzX2NsZWFuX3JldGVzdF9kYXRhX2V6KQpgYGAKCiMjIEVGQSBvbiBIRERNIHZhcmlhYmxlcyBvZiByZXRlc3QgZGF0YQoKYGBge3J9CnJldGVzdF9kYXRhX2hkZG0gPSByZXRlc3RfZGF0YSAlPiUKICBzZWxlY3QoZ3JlcCgnaGRkbScsIG5hbWVzKHJldGVzdF9kYXRhKSwgdmFsdWU9VCkpCmBgYAoKUmVtb3ZlIHZhcmlhYmxlcyB0aGF0IGFyZSBjb3JyZWxhdGVkID4wLjg1CgpgYGB7cn0KY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbSA9IHJlbW92ZV9jb3JyZWxhdGVkX3Rhc2tfdmFyaWFibGVzKHJldGVzdF9kYXRhX2hkZG0pCmBgYAoKUmVtb3ZlIG91dGxpZXJzICg+Mi41IFNEIGF3YXkpCgpgYGB7cn0KY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbSA9IGFzLmRhdGEuZnJhbWUoYXBwbHkoY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbSwgMiwgcmVtb3ZlX291dGxpZXJzKSkKYGBgCgpUcmFuc2Zvcm0gc2tld2VkIHZhcmlhYmxlcyBhcyBpbiBUMQoKYGBge3J9CmNsZWFuX3JldGVzdF9kYXRhX2hkZG0gPSBjbGVhbl9yZXRlc3RfZGF0YV9oZGRtICU+JQogIG11dGF0ZShkb3RfcGF0dGVybl9leHBlY3RhbmN5LmhkZG1fdGhyZXNoLmxvZ1RyID0gbG9nKGRvdF9wYXR0ZXJuX2V4cGVjdGFuY3kuaGRkbV90aHJlc2gpLApzdGltX3NlbGVjdGl2ZV9zdG9wX3NpZ25hbC5oZGRtX3RocmVzaC5sb2dUciA9IGxvZyhzdGltX3NlbGVjdGl2ZV9zdG9wX3NpZ25hbC5oZGRtX3RocmVzaCkpICU+JQogIHNlbGVjdCgtZG90X3BhdHRlcm5fZXhwZWN0YW5jeS5oZGRtX3RocmVzaCwtc3RpbV9zZWxlY3RpdmVfc3RvcF9zaWduYWwuaGRkbV90aHJlc2gpCmBgYAoKRHJvcCBzdWJqZWN0IGlkZW50aWZpZXIgY29sdW1uLCBtZWFuIGltcHV0ZSBhbmQgZHJvcCBjb2xzIHdpdGggbm8gdmFyaWFuY2UKCmBgYHtyfQpjbGVhbl9yZXRlc3RfZGF0YV9oZGRtX3N0ZCA9IGNsZWFuX3JldGVzdF9kYXRhX2hkZG0gJT4lIG11dGF0ZV9pZihpcy5udW1lcmljLCBzY2FsZSkKCiNtZWFuIGltcHV0YXRpb24KY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbV9zdGRbaXMubmEoY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbV9zdGQpXT0wCgojZHJvcCBjb2xzIHdpdGggbm8gdmFyaWFuY2UKY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbV9zdGQgPSBjbGVhbl9yZXRlc3RfZGF0YV9oZGRtX3N0ZCAlPiUKICBzZWxlY3RfaWYoZnVuY3Rpb24oY29sKSBzZChjb2wpICE9IDApCmBgYAoKUmVzaWR1YWxpemUgQWdlIGFuZCBTZXggZWZmZWN0cyAKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmNsZWFuX3JldGVzdF9kYXRhX2hkZG1fc3RkID0gY2JpbmQoY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbV9zdGQsIGRlbW9ncmFwaGljc1ssYygiQWdlIiwgIlNleCIpXSkKCnJlc19jbGVhbl9yZXRlc3RfZGF0YV9oZGRtID0gcmVzaWR1YWxpemVfYmFzZWxpbmUoY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbV9zdGQpCmBgYAoKU2VsZWN0IG9ubHkgdGhlIHZhcnMgdGhhdCB3ZW50IGluIHRvIHRoZSBsaW1pdGVkIHNldCBvZiBoZGRtIHZhcmlhYmxlcwoKYGBge3J9CnJlc19jbGVhbl9yZXRlc3RfZGF0YV9oZGRtX3N1YnNldCA9IHJlc19jbGVhbl9yZXRlc3RfZGF0YV9oZGRtICU+JQogIHNlbGVjdChoZGRtX3Zhcl9zdWJzZXQpCmBgYAoKUHJlZGljdCB1c2luZyB0aGUgMyBmYWN0b3IgRUZBIGZyb20gVDEKCmBgYHtyfQpoZGRtX3QyX2ZhXzNfc3Vic2V0ID0gcHJlZGljdChoZGRtX3QxX2ZhXzNfc3Vic2V0LCByZXNfY2xlYW5fcmV0ZXN0X2RhdGFfaGRkbV9zdWJzZXQpCmBgYAo=